package com.autonavi.yunda.yunji.core.service;

import com.autonavi.yunda.yunji.common.exception.AmapPreconditions;
import com.autonavi.yunda.yunji.common.utils.JsonUtils;
import com.autonavi.yunda.yunji.common.utils.http.HttpClient;
import com.autonavi.yunda.yunji.common.utils.http.vo.HttpResponse;
import com.autonavi.yunda.yunji.core.config.ApplicationEnvConfig;
import com.autonavi.yunda.yunji.core.enums.InterfaceStatus;
import com.autonavi.yunda.yunji.core.model.condition.TaskConfigQueryCondition;
import com.autonavi.yunda.yunji.core.model.dto.ProjectDTO;
import com.autonavi.yunda.yunji.core.model.dto.TaskConfigDTO;
import com.autonavi.yunda.yunji.core.model.mappers.TaskConfigMapper;
import com.autonavi.yunda.yunji.core.vo.EngineInterfaceConfigVO;
import com.autonavi.yunda.yunji.core.vo.ProjectExtraData;
import com.autonavi.yunda.yunji.core.vo.response.TaskConfigPageVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.autonavi.yunda.yunji.common.constant.CommonConstant.HEADER_TRACE_ID;
import static com.autonavi.yunda.yunji.common.constant.CommonConstant.MDC_TRACE_ID;

/**
 * @author cizhong.wcs
 * @date 2022/3/7 7:37 下午
 */
@Slf4j
@Component
public class TaskConfigService implements ITaskConfigService {
    @Resource
    TaskConfigMapper taskConfigMapper;
    @Resource
    ApplicationEnvConfig applicationEnvConfig;
    @Resource
    ProjectService projectService;
    @Resource
    public SkylineIpServiceImpl skylineIpService;
    static HttpClient httpClient = new HttpClient();

    @Override
    public EngineInterfaceConfigVO findEngineTaskConfigVO(String taskName) {
        TaskConfigDTO dto = fetchByName(taskName);
        if (dto == null) {
            return null;
        }
        EngineInterfaceConfigVO vo = new EngineInterfaceConfigVO();
        BeanUtils.copyProperties(dto, vo);
        ProjectDTO project = projectService.findByProjectId(dto.getProjectId());
        ProjectExtraData projectExtraData = (project != null && project.getExtraData() != null) ? project.getExtraData() : new ProjectExtraData();
        vo.setProjectExtraData(projectExtraData);
        return vo;
    }

    @Override
    public List<String> listNewPublishedTasks(Collection<String> existsTaskNames) {
        QueryWrapper<TaskConfigDTO> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("status", Lists.newArrayList(InterfaceStatus.PUBLISHED));
        if (!existsTaskNames.isEmpty()) {
            queryWrapper.notIn("name", existsTaskNames);
        }
        return taskConfigMapper.selectList(queryWrapper)
                .stream().map(TaskConfigDTO::getName)
                .collect(Collectors.toList());
    }

    public TaskConfigPageVO listByCondition(TaskConfigQueryCondition condition) {
        if (condition.projectIds.isEmpty()) {
            return new TaskConfigPageVO();
        }
        Page<TaskConfigDTO> page = new Page<>(condition.getPageNo(), condition.getPageSize());
        taskConfigMapper.selectPage(page, getWrapper(condition));
        List<ProjectDTO> projectDTOS = projectService.findByProjectIds(page.getRecords().stream().map(TaskConfigDTO::getProjectId).collect(Collectors.toList()));
        return new TaskConfigPageVO(page.getRecords(), projectDTOS, page.getTotal());
    }

    public Long add(TaskConfigDTO dto) {
        LambdaQueryWrapper<TaskConfigDTO> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(TaskConfigDTO::getName, dto.getName());
        TaskConfigDTO sameNameTask = fetchByName(dto.getName());
        AmapPreconditions.checkArgumentForUser(sameNameTask == null, "创建失败,任务名称已存在");
        taskConfigMapper.insert(dto);
        return dto.getId();
    }

    public void update(UpdateWrapper<TaskConfigDTO> updateWrapper) {
        taskConfigMapper.update(null, updateWrapper);
    }

    public TaskConfigDTO findByIdOrThrow(Long id) {
        TaskConfigDTO dto = taskConfigMapper.selectById(id);
        AmapPreconditions.checkArgumentForUser(dto != null, "不存在该配置 id = " + id);
        return dto;
    }

    private TaskConfigDTO fetchByName(String name) {
        AmapPreconditions.checkNotBlank(name);
        LambdaQueryWrapper<TaskConfigDTO> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(TaskConfigDTO::getName, name);
        List<TaskConfigDTO> tasks = taskConfigMapper.selectList(wrapper);
        if (tasks.isEmpty()) {
            return null;
        }
        return tasks.get(0);
    }

    private QueryWrapper<TaskConfigDTO> getWrapper(TaskConfigQueryCondition condition) {
        QueryWrapper<TaskConfigDTO> wrapper = new QueryWrapper<>();
        if (null != condition.status) {
            wrapper.like("status", condition.status);
        }
        if (StringUtils.isNotBlank(condition.name)) {
            wrapper.like("name", condition.name);
        }
        wrapper.orderByDesc("id");
        return wrapper;
    }

    public void updateStatus(Long id, InterfaceStatus interfaceStatus) {
        UpdateWrapper<TaskConfigDTO> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("id", id);
        updateWrapper.set("status", interfaceStatus);
        taskConfigMapper.update(null, updateWrapper);
    }

    public Long enterValidating(TaskConfigDTO validatingDTO) {
        TaskConfigDTO existsDto = fetchByName(validatingDTO.getName());
        if (existsDto == null) {
            taskConfigMapper.insert(validatingDTO);
        } else {
            AmapPreconditions.checkArgumentForUser(!((existsDto.getStatus().equals(InterfaceStatus.PUBLISHED)) && applicationEnvConfig.isPublish()),
                    "该任务在生产环境已经发布,若要覆盖请先下线该任务");
            validatingDTO.setId(existsDto.getId());
            taskConfigMapper.updateById(validatingDTO);
        }
        return validatingDTO.getId();
    }

    public void publish(Long id) {
        TaskConfigDTO dto = findByIdOrThrow(id);
        AmapPreconditions.checkArgumentForUser(!dto.getStatus().equals(InterfaceStatus.PUBLISHED), "接口任务已发布过,无需再发布");
        AmapPreconditions.checkArgumentForUser(
                dto.getStatus().equals(InterfaceStatus.TEST_SUCCESS), "接口调试成功才能进行发布,当前状态:" + dto.getStatus().getDesc());
        updateStatus(id, InterfaceStatus.PUBLISHED);
    }

    public void offline(Long id) {
        TaskConfigDTO dto = findByIdOrThrow(id);
        AmapPreconditions.checkArgumentForUser(dto.getStatus().equals(InterfaceStatus.PUBLISHED), "发布中的接口才能下线");
        updateStatus(id, InterfaceStatus.OFFLINE);
    }

    public void notifyExecutors() {
        try {
            List<String> ipList = skylineIpService.fetchJobExecutorIp();
            for (String ip : ipList) {
                try {
                    HttpResponse res = httpClient.get(
                            "http://" + ip + "/checkAndRefreshExecutorInfo",
                            Map.of(HEADER_TRACE_ID, MDC.get(MDC_TRACE_ID) == null ? "" : MDC.get(MDC_TRACE_ID))
                    );
                    log.info("notifyExecutors ip =  {},res = {}", ip, JsonUtils.toString(res));
                } catch (Exception e) {
                    log.error("notifyExecutors error, ip =  {}", ip, e);
                }
            }
        } catch (Exception e) {
            log.error("notifyExecutors error ", e);
        }
    }

}
